package com.aabte.nettyleran.netty.http.xml;

import com.aabte.nettyleran.netty.http.xml.entity.Address;
import com.aabte.nettyleran.netty.http.xml.entity.Order;
import com.aabte.nettyleran.netty.http.xml.httpdecoder.HttpXmlRequestDecoder;
import com.aabte.nettyleran.netty.http.xml.httpencoder.HttpXmlResponseEncoder;
import com.google.common.collect.Lists;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.*;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;

import java.nio.charset.StandardCharsets;

import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE;
import static io.netty.handler.codec.http.HttpHeaderUtil.isKeepAlive;
import static io.netty.handler.codec.http.HttpResponseStatus.INTERNAL_SERVER_ERROR;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;

/**
 * @author Daniel
 * @version 1.0
 * @date 2020/3/14
 */
public class HttpXmlServer {

    public static void main(String[] args) throws InterruptedException {
        int port = 8080;
        if (args != null && args.length > 0) {
            try {
                Integer.valueOf(args[0]);
            } catch (NumberFormatException e) {
                e.printStackTrace();
            }
        }

        new HttpXmlServer().run(port);
    }

    private void run(int port) throws InterruptedException {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel sc) throws Exception {
                        sc.pipeline().addLast("http-decoder", new HttpRequestDecoder());
                        sc.pipeline().addLast("http-aggregator", new HttpObjectAggregator(65535));
                        sc.pipeline().addLast("xml-decoder", new HttpXmlRequestDecoder(Order.class, true));
                        sc.pipeline().addLast("http-encoder", new HttpResponseEncoder());
                        sc.pipeline().addLast("xml-encoder", new HttpXmlResponseEncoder());
                        sc.pipeline().addLast("xml-server-handler", new HttpXmlServerHandler());
                    }
                });

        try {
            ChannelFuture channelFuture = serverBootstrap.bind(port).sync();
            channelFuture.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    private static class HttpXmlServerHandler extends SimpleChannelInboundHandler<HttpXmlRequest> {
        @Override
        protected void messageReceived(ChannelHandlerContext ctx, HttpXmlRequest xmlRequest) throws Exception {
            FullHttpRequest request = xmlRequest.getRequest();
            Order order = (Order) xmlRequest.getBody();
            System.out.println("Http server receive request: " + order);
            doBusiness(order);
            ChannelFuture channelFuture = ctx.writeAndFlush(new HttpXmlResponse(null, order));
            if (!isKeepAlive(request)) {
                channelFuture.addListener(new GenericFutureListener<Future<? super Void>>() {
                    @Override
                    public void operationComplete(Future<? super Void> future) throws Exception {
                        ctx.close();
                    }
                });
            }
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            cause.printStackTrace();
            if (ctx.channel().isActive()) {
                sendError(ctx, INTERNAL_SERVER_ERROR);
            }
        }

        private void sendError(ChannelHandlerContext ctx, HttpResponseStatus status) {
            ByteBuf byteBuf = Unpooled.copiedBuffer("Failure: " + status.toString() + "\r\n", StandardCharsets.UTF_8);
            DefaultFullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, status, byteBuf);
            response.headers().set(CONTENT_TYPE, "text/plain;charset=UTF-8");
            ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
        }

        private void doBusiness(Order order) {
            order.getCustomer().setFistName("狄");
            order.getCustomer().setLastName("仁杰");
            order.getCustomer().setMiddleNames(Lists.newArrayList("怀英"));

            Address address = order.getBillTo();
            address.setCity("洛阳");
            address.setCountry("大唐");
            address.setState("河南道");
            address.setPostCode("123456");

            order.setBillTo(address);
            order.setShipTo(address);
        }
    }
}
